﻿using System;
using System.Diagnostics;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Transforms;

// [UpdateAfter(typeof(EndSimulationEntityCommandBufferSystem))]
public class MinoSpawnerSystem : SystemBase
{
    public Entity gameEntity;

    [Serializable]
    struct MinoPosConfig {
        public int3[] shape;
        public MinoRotateType rotateType;
    }

    const int minoTypeCount = 7;
    MinoPosConfig[] minoPosConfigs;
    Unity.Mathematics.Random random;
    int previewIdx;
    Entity minoPfb;
    float3 previewOffset, startOffset;
    EntityQuery gameGoQuery, previewQuery;

    //protected override void OnCreate() {
    //    base.OnCreate();
    //    gameGoQuery = GetEntityQuery(ComponentType.ReadOnly<MinoGridEvent>());
    //    previewQuery = GetEntityQuery(typeof(MinoState), typeof(MinoBornTime), typeof(Translation), ComponentType.ReadOnly<LocalPosition>());
    //    previewIdx = -1;
    //}

    public void Retry() {
        previewIdx = -1;
    }

    protected override void OnStartRunning() {
        base.OnStartRunning();

        gameGoQuery = GetEntityQuery(ComponentType.ReadOnly<MinoGridEvent>());
        previewQuery = GetEntityQuery(ComponentType.ReadOnly<PreviewMinoTag>(), typeof(Translation), ComponentType.ReadOnly<LocalPosition>());
        previewIdx = -1;

        minoPosConfigs = new MinoPosConfig[] { 
            new MinoPosConfig{ shape = new int3[]{ new int3(-1, -1, 0), new int3(0, -1, 0), new int3(0, 0, 0), new int3(0, 1, 0) }, rotateType = MinoRotateType.FourTimes },  // J
            new MinoPosConfig{ shape = new int3[]{ new int3( 1, -1, 0), new int3(0, -1, 0), new int3(0, 0, 0), new int3(0, 1, 0) }, rotateType = MinoRotateType.FourTimes},   // L
            new MinoPosConfig{ shape = new int3[]{ new int3(-2,  0, 0), new int3(-1, 0, 0), new int3(0, 0, 0), new int3(1, 0, 0) }, rotateType = MinoRotateType.TwoTimes},    // Long
            new MinoPosConfig{ shape = new int3[]{ new int3(-1,  0, 0), new int3(0,  0, 0), new int3(0, 1, 0), new int3(1, 1, 0) }, rotateType = MinoRotateType.TwoTimes},    // S
            new MinoPosConfig{ shape = new int3[]{ new int3( 0,  0, 0), new int3(1,  0, 0), new int3(0, 1, 0), new int3(1, 1, 0) }, rotateType = MinoRotateType.ZeroTimes},   // Squre
            new MinoPosConfig{ shape = new int3[]{ new int3(-1,  0, 0), new int3(0,  0, 0), new int3(1, 0, 0), new int3(0, 1, 0) }, rotateType = MinoRotateType.FourTimes},   // T
            new MinoPosConfig{ shape = new int3[]{ new int3(-1,  1, 0), new int3(0,  1, 0), new int3(0, 0, 0), new int3(1, 0, 0) }, rotateType = MinoRotateType.TwoTimes }    // Z
        };

        previewOffset = new float3(-3f, 18.5f, 0f);
        startOffset = new float3(6f, 22f, 0f);
        random = new Unity.Mathematics.Random((uint)new System.Random().Next());
    }

    protected override void OnUpdate() {
        // Get mino prefab
        if (!EntityManager.Exists(minoPfb)) {
            var pfbQuery = GetEntityQuery(ComponentType.ReadOnly<TetrominoPfbComponent>());
            if (pfbQuery.CalculateEntityCount() > 0) { 
                var pfbComponent = pfbQuery.ToComponentDataArray<TetrominoPfbComponent>(Allocator.Temp);
                minoPfb = pfbComponent[0].pfbMino;
                pfbComponent.Dispose();
            }
        } else { 
            var mgEventArray = gameGoQuery.ToComponentDataArray<MinoGridEvent>(Allocator.Temp);
            if (mgEventArray.Length > 0 && mgEventArray[0].Value == MinoGridEventType.CreateTetromino) {
                bool noPreviewBefore = previewIdx < 0;
                var curIdx = noPreviewBefore ? random.NextInt(minoTypeCount) : previewIdx;
                previewIdx = random.NextInt(minoTypeCount);
                
                /// 如果之前没有 Preview
                /// - 创建 Preview
                /// - 创建 Dynamic
                /// 如果之前有 Preview
                /// - 把之前的 Preview 改为 Dynamic
                /// - 创建 Preview
                
                /// Preview & Dynamic 的差别
                /// - Translation
                /// - MinoState
                /// - MinoBornTime
                /// 
                float curTime = (float)Time.ElapsedTime;
                if (noPreviewBefore) {
                    CreateMino(curIdx, startOffset, false, curTime);
                } else {
                    var tmpEntities = previewQuery.ToEntityArray(Allocator.TempJob);
                    var localPos = previewQuery.ToComponentDataArray<LocalPosition>(Allocator.TempJob);
                    for (int i = 0, iCount = tmpEntities.Length; i < iCount; i++) {
                        EntityManager.RemoveComponent<PreviewMinoTag>(tmpEntities[i]);
                        EntityManager.AddComponentData(tmpEntities[i], new DynamicMinoTag());
                        EntityManager.SetComponentData(tmpEntities[i], new Translation { Value = localPos[i].Value + startOffset });
                    }
                    tmpEntities.Dispose();
                    localPos.Dispose();
                }
                // 创建 Preview
                CreateMino(previewIdx, previewOffset);

                EntityManager.SetComponentData(gameEntity, new MinoGridEvent { Value = MinoGridEventType.Idle });
                EntityManager.SetComponentData(gameEntity, new MinoBornTime { Value = curTime });
                EntityManager.SetComponentData(gameEntity, new AutoMoveTimeStamp { Value = 0f });
                var istQueue = EntityManager.GetBuffer<MinoInstraction>(gameEntity);
                istQueue.Add(new MinoInstraction { Value = InstractionType.Spawn });
            }
            mgEventArray.Dispose();
        }
    }

    private void CreateMino(int pfbIdx_, float3 offsetPos_, bool isPreview = true, float bornTime_ = -1f) { 
        for (int i = 0, iCount = minoPosConfigs[pfbIdx_].shape.Length; i < iCount; i++) {
            var minoEntity = EntityManager.Instantiate(minoPfb);
            EntityManager.SetComponentData(minoEntity, new LocalPosition { Value = minoPosConfigs[pfbIdx_].shape[i] });
            EntityManager.SetComponentData(minoEntity, new Translation { Value = minoPosConfigs[pfbIdx_].shape[i] + offsetPos_ });
            EntityManager.SetComponentData(minoEntity, new MinoRotationData { typeValue = minoPosConfigs[pfbIdx_].rotateType });
            if (isPreview) {
                EntityManager.AddComponentData(minoEntity, new PreviewMinoTag());
            } else {
                EntityManager.AddComponentData(minoEntity, new DynamicMinoTag());
            }
        }
    }
}
